# This file is part of the AutoMAT distribution (https://bitbucket.com/mahomaho/AutoMAT).
# Copyright (c) 2021 Mattias Holmqvist.
# 
# AutoMAT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# AutoMAT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with AutoMAT.  If not, see <https://www.gnu.org/licenses/>.

from AutoMAT import *
from AutoMAT import complexbase
import argparse
from importlib.machinery import SourceFileLoader
import os,sys

num_errors = 0

_rulefiles=[]

def validate(val,ignore_modules):
	global num_errors
	try:
		val.validate()
	except AssertionError as e:
		print('Error in',val,':',e)
		num_errors += 1
	if isinstance(val,autosar_r4p0.Group.EcucContainerValue):
		aggregations=val.ecucAggregations()
	elif isinstance(val,autosar_r4p0.Group.EcucModuleConfigurationValues):
		if ignore_modules:
			return
		aggregations=val.ecucAggregations()
	elif isinstance(val,complexbase.ComplexTypeBase):
		if ignore_modules and isinstance(val,autosar_r4p0.Group.EcucModuleDef):
			return
		aggregations=val.aggregations()
	else:
		return
	for aggregation in aggregations:
		try:
			aggregation.validate()
		except AssertionError as e:
			name = aggregation.name()
			print('Error in',str(val)+'/'+name,':',e)
			num_errors += 1
		for child in aggregation:
			validate(child, ignore_modules)

def load_rules(root):
	if root.moduleDescription.ref().usedCodeGenerator:
		path = os.path.dirname(root.moduleDescription.ref().usedCodeGenerator.file()[0])
		rules=[e.strip()[5:].strip() for e in root.moduleDescription.ref().usedCodeGenerator.val().split(',') if e.strip().startswith('RULE:')]
		for rulefile in rules:
			if rulefile not in _rulefiles:
				rulefile = os.path.join(path,rulefile)
				ruledir = os.path.dirname(rulefile)
				ruledir_in_path = ruledir in sys.path
				if not ruledir_in_path:
					sys.path.append(ruledir)
				basepath = os.path.splitext(rulefile)[0]
				scriptname=os.path.basename(basepath)
				from importlib.machinery import EXTENSION_SUFFIXES
				for suffix in EXTENSION_SUFFIXES:
					if os.path.exists(basepath+suffix):
						rulefile = basepath+suffix
						from importlib.machinery import ExtensionFileLoader
						fileLoader = ExtensionFileLoader
						break
				else:
					fileLoader = SourceFileLoader
				rulemodule = fileLoader(scriptname, rulefile).load_module()
				if hasattr(rulemodule,'main'):
					rulemodule.main('')
				del sys.modules[scriptname]
				if not ruledir_in_path:
					sys.path.remove(ruledir)
				_rulefiles.append(rulefile)

def main(argv):
	global num_errors
	_parser = argparse.ArgumentParser(description='Autosar model validator from AutoMAT')
	#_parser.add_argument('-o', '--outputFile', nargs='?', help='file to save merged model to, default files with extension replaced to arxml are created', default=None)
	_parser.add_argument('-r', '--validationroot', nargs='?', help='path to EcucModuleConfigurationValues to be validated, or all loaded if not specified.', default=None)
	_parser.add_argument('-d', '--validate_def', nargs='?', help='path to EcucModuleConfigurationDef to be validated, all EcucModuleConfigurationValues using def will be validated.', default=None)
	_parser.add_argument('-L', dest='load_rules', action='store_true', help="Load rules files specified in implementation")
	_parser.add_argument('-N', dest='no_validation', action='store_true', help="Do not validate the EcuC configuration, just load rules")
	_parser.add_argument('-a', dest='validate_all', action='store_true', help="Validate the autosar model")
	_args = _parser.parse_args(argv)
	valdate_all = True
	if _args.validationroot:
		valdate_all = False
		root=_args.validationroot.strip('/').split('/')
		root=eval('autosar.'+'.'.join(root))
		if _args.load_rules:
			load_rules(root)
		if not _args.no_validation:
			validate(root, False)
	if _args.validate_def:
		valdate_all = False
		definition=eval('autosar.'+'.'.join(_args.validate_def.strip('/').split('/')))
		for root in definition.references().parent().select.findall(lambda e:isinstance(e, autosar_r4p0.EcucModuleConfigurationValues)):
			prev_errors = num_errors
			if _args.load_rules:
				load_rules(root)
			if not _args.no_validation:
				print(f'\nValidation of module {root}:')
				validate(root, False)
				if prev_errors != num_errors:
					print(f'Validation of {root} failed with {num_errors - prev_errors} errors')
				else:
					print(f'Validation of {root} done')
	if valdate_all:
		for root in autosar_r4p0.EcucModuleConfigurationValues.instances():
			prev_errors = num_errors
			if _args.load_rules:
				load_rules(root)
			if not _args.no_validation:
				print(f'\nValidation of module {root}:')
				validate(root, False)
				if prev_errors != num_errors:
					print(f'Validation of {root} failed with {num_errors - prev_errors} errors')
				else:
					print(f'Validation of {root} done')
	if _args.validate_all:
		prev_errors = num_errors
		print(f'\nValidation of AUTOSAR model:')
		validate(autosar, True)
		if prev_errors != num_errors:
			print(f'Validation failed with {num_errors - prev_errors} errors')
		else:
			print(f'Validation of AUTOSAR model done')
	if num_errors:
		print(f'\nValidation failed with total {num_errors} errors\n')
	return num_errors
	
